let Camera = require('./camera');
let {vec3, mat4} = require('gl-matrix');
let InputMgr = require('./inputMgr');
let {codes} = require('keycode');
let textureUtils = require('./textureUtils');

let shaderUtils = require('./shaderUtils');
let objParser = require('./objParser');
let GameItem = require('./gameItem');
let ScreenItem = require('./screenItem');
let SimpleGameItem = require('./SimpleGameItem');

const step = 0.4;



let resCache = require('./res/resCache');
let ResItem = require('./res/resItem');
// let ResType = require('./res/resType');

let OrthoCamera = require('./orthoCamera');


//  定义资源path
const PreVsItem = new ResItem('assets/pre.vs.glsl');
const PreFsItem = new ResItem('assets/pre.fs.glsl');
const PlaneObjItem = new ResItem('assets/plane.obj');
const BallObjItem = new ResItem('assets/ball.obj');
const DepthVsItem = new ResItem('assets/depth.vs.glsl');
const DepthFsItem = new ResItem('assets/depth.fs.glsl');
const NormalVsItem = new ResItem('assets/normal.vs.glsl');
const NormalFsItem = new ResItem('assets/normal.fs.glsl');


// let smProjMatrix = mat4.create();
// let smViewMatrix = mat4.create();
//
// globalThis.v = 5;
// let smPos = vec3.fromValues(v, v, v);
// let smTarget = vec3.create();
// let smUp = vec3.fromValues(0, 1, 0);


const shadowMapWidth = 1024;
const shadowMapHeight = 1024;
let y = 0;
let scene = {
    app: null,
    camera: null,
    orhtorCamera: null, //
    inputMgr: null,
    items: [],

    ambientColor: vec3.fromValues(0.3, 0.3, 0.3),
    dirLight: {
        dir: vec3.fromValues(1, 1, 1),
        color: vec3.fromValues(1, 1, 1),
        shiness: 20,
    },

    loadRes() {
        resCache.addRes(PreVsItem);
        resCache.addRes(PreFsItem);
        resCache.addRes(PlaneObjItem);
        resCache.addRes(BallObjItem);
        resCache.addRes(DepthVsItem);
        resCache.addRes(DepthFsItem);
        resCache.addRes(NormalVsItem);
        resCache.addRes(NormalFsItem);

        // resCache.addRes(new ResItem([
        //     'assets/skybox/+x.png', 'assets/skybox/-x.png',
        //     'assets/skybox/+y.png', 'assets/skybox/-y.png',
        //     'assets/skybox/+z.png', 'assets/skybox/-z.png',
        // ], ResType.CubeImage));
    },

    init(app) {

        this.app = app;
        let gl = app.gl;
        gl.clearColor(0, 0, 0, 1);

        // init shader
        let normalShader = this.normalShader = shaderUtils.buildShader(NormalVsItem.data, NormalFsItem.data);
        shaderUtils.createAttribs(normalShader, ['a_position', 'a_normal']);
        shaderUtils.createUniforms(normalShader, [
            'projMatrix',
            'mvMatrix',
            'nvMatrix',
            'viewMatrix',
            'lightMVPMatrix',
            'u_ambientColor',
            'u_dirLight.dir',
            'u_dirLight.color',
            'u_dirLight.shiness',
            'u_sampler0',
            'u_color']);

        // depthShader
        let depShader = this.depShader = shaderUtils.buildShader(DepthVsItem.data, DepthFsItem.data);
        shaderUtils.createAttribs(depShader, ['a_position']);
        shaderUtils.createUniforms(depShader, ['projMatrix', 'mvMatrix']);

        // preShaderInfo
        // let preVsItem = resCache.getRes(ResPreVs);
        // let preFsItem = resCache.getRes(ResPreFs);
        // let preShader = this.preShader = shaderUtils.buildShader(preVsItem.data,preFsItem.data);
        // shaderUtils.createAttribs(preShader,['a_position']);
        // shaderUtils.createUniforms(preShader,['projMatrix','mvMatrix','near','far']);

        // init obj
        let cubeData = objParser.parse(BallObjItem.data);

        // 普通 item
        let cubeItem = new GameItem(normalShader, cubeData);
        mat4.fromTranslation(cubeItem.transMat, vec3.fromValues(0, 6, 0));
        mat4.fromScaling(cubeItem.scaleMat, vec3.fromValues(4, 4, 4));
        this.items.push(cubeItem);

        let cubeItem2 = new GameItem(normalShader, cubeData);
        mat4.fromTranslation(cubeItem2.transMat, vec3.fromValues(4, 10, 6));
        mat4.fromScaling(cubeItem2.scaleMat, vec3.fromValues(2, 2, 2));
        cubeItem2.color[2] = 1;
        this.items.push(cubeItem2);

        // 创造一个场景
        // 一个地面 + 一个Cube
        // ground
        let groundData = objParser.parse(PlaneObjItem.data);
        let groundItem = new GameItem(normalShader, groundData);
        mat4.fromScaling(groundItem.scaleMat, vec3.fromValues(20, 20, 20));
        groundItem.color[0] = 1.0;
        groundItem.color[1] = 1.0;
        this.items.push(groundItem);

        // instance Item
        // let insItem = new InstanceItem(shaderInfo,cubeData);
        // this.items.push(insItem);

        // init camera
        let pos = vec3.fromValues(-5, 10, 30);
        let target = vec3.fromValues(0, 0, 0);
        let up = vec3.fromValues(0, 1, 0);
        this.camera = new Camera(pos, target, up);

        let lightPos = vec3.create();
        vec3.scale(lightPos,this.dirLight.dir,10);
        this.orhtorCamera = new OrthoCamera(lightPos);

        // init input
        this.inputMgr = new InputMgr(this);

        // init fbo
        this.fbo = this.createFBO();
    },

    createFBO() {
        let gl = this.app.gl;

        let fbo = gl.createFramebuffer();

        let texId = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texId);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, shadowMapWidth, shadowMapHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        fbo.texId = texId;

        // 新建渲染缓冲区对象作为帧缓冲区的深度缓冲区对象
        let depthBuffer = gl.createRenderbuffer();
        gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
        gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, shadowMapWidth, shadowMapHeight);


        // 关联深度缓冲区
        gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texId, 0);
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);

        // 检查 framebuffer 是否配置成功
        let fboState = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
        if (fboState != gl.FRAMEBUFFER_COMPLETE) {
            throw `config fbo error! fboState:${fboState}`
        }

        // 绑定到默认的帧缓冲
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        gl.bindTexture(gl.TEXTURE_2D, null);
        gl.bindRenderbuffer(gl.RENDERBUFFER, null);

        return fbo;
    },

    update(dt) { // eslint-disable-line
        this.camera.calProjMatrix(this.app.width, this.app.height);
        this.camera.calViewMatrix();

        y++;
        if (y > 360) y -= 360;

        let cube = this.items[0];
        mat4.fromYRotation(cube.rotateMat, y * Math.PI / 180);

        //
        this.items.forEach(item=>{
            item.updateWorldMatrixs();
        });
    },
    render() {
        let gl = globalThis.app.gl;
        let camera = this.camera;

        // 第一步  在灯光的位置去渲染,存储深度缓存
        this.orhtorCamera.calProjMatrix(this.app.width, this.app.height);
        this.orhtorCamera.calViewMatrix();

        gl.bindFramebuffer(gl.FRAMEBUFFER,this.fbo);
        gl.viewport(0,0,shadowMapWidth,shadowMapHeight);
        gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
        this.items.forEach(item => {
            item.depthPass(this.depShader, this.orhtorCamera);
        });


        // 正常绘制
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        gl.viewport(0, 0, this.app.width, this.app.height);
        gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
        gl.useProgram(this.normalShader.program);

        // light info
        gl.uniform3fv(this.normalShader.uniforms['u_ambientColor'], this.ambientColor);
        gl.uniform3fv(this.normalShader.uniforms['u_dirLight.dir'], this.dirLight.dir);
        gl.uniform3fv(this.normalShader.uniforms['u_dirLight.color'], this.dirLight.color);
        gl.uniform1f(this.normalShader.uniforms['u_dirLight.shiness'], this.dirLight.shiness);

        // projMatrix
        gl.uniformMatrix4fv(this.normalShader.uniforms['projMatrix'], false, camera.projMatrix);

        // 纹理
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, this.fbo.texId);
        gl.uniform1i(this.normalShader.uniforms['u_sampler0'], 0);

        this.items.forEach(item => {
            item.render(camera, this);
        });
    },


    //-------------------------------------------
    // key events
    onKeyDown(event) {
        let camera = this.camera;
        switch (event.keyCode) {
            case codes.s:
                camera.translateZ(-step);
                break;
            case codes.w:
                camera.translateZ(step);
                break;
            case codes.a:
                camera.translateX(step);
                break;
            case codes.d:
                camera.translateX(-step);
                break;
        }
    },
    onKeyUp(event) { // eslint-disable-line
    },

    onMouseDown(x, y) {
        console.log(`onMouseDown x:${x},y:${y}`);
    },
    onMouseUp(x, y) {
        console.log(`onMouseUp x:${x},y:${y}`);
    }
};

module.exports = scene;
